﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.Algorithm;
using NetTopologySuite.Geometries;

namespace NetTopologySuite.Operation.Overlay.Validate
{
	public class FuzzyPointLocator
	{
		public FuzzyPointLocator(IGeometry g, double boundaryDistanceTolerance)
		{
			this._g = g;
			this._boundaryDistanceTolerance = boundaryDistanceTolerance;
			this._linework = FuzzyPointLocator.ExtractLinework(g);
		}

		public Location GetLocation(Coordinate pt)
		{
			if (this.IsWithinToleranceOfBoundary(pt))
			{
				return Location.Boundary;
			}
			return this._ptLocator.Locate(pt, this._g);
		}

		private static IMultiLineString ExtractLinework(IGeometry g)
		{
			PolygonalLineworkExtracter polygonalLineworkExtracter = new PolygonalLineworkExtracter();
			g.Apply(polygonalLineworkExtracter);
			List<ILineString> linework = polygonalLineworkExtracter.Linework;
			return g.Factory.CreateMultiLineString(linework.ToArray());
		}

		private bool IsWithinToleranceOfBoundary(Coordinate pt)
		{
			for (int i = 0; i < this._linework.NumGeometries; i++)
			{
				ICoordinateSequence coordinateSequence = ((ILineString)this._linework.GetGeometryN(i)).CoordinateSequence;
				for (int j = 0; j < coordinateSequence.Count - 1; j++)
				{
					coordinateSequence.GetCoordinate(j, this._seg.P0);
					coordinateSequence.GetCoordinate(j + 1, this._seg.P1);
					if (this._seg.Distance(pt) <= this._boundaryDistanceTolerance)
					{
						return true;
					}
				}
			}
			return false;
		}

		private readonly IGeometry _g;

		private readonly double _boundaryDistanceTolerance;

		private readonly IMultiLineString _linework;

		private readonly PointLocator _ptLocator = new PointLocator();

		private readonly LineSegment _seg = new LineSegment();
	}
}
